﻿using System.Collections.Generic;
using System.IO;
using System.Text;
using KSS.Generator.Configuration;
using KSS.Generator.Schema;

namespace KSS.Generator
{
    public class ManagerProjectGenerator : ProjectGenerator
    {
        public override string ProjectName
        {
            get { return Template.Project.ManagerProjectName; }
        }

        protected override void GenerateFile(ProjectSetting projectConfig, FileInfo templateFile, DirectoryInfo generatedDirectory)
        {
            switch (templateFile.Name)
            {
                case Template.ManagerProject.GeneratedManagerFileName:
                    GenerateManagerFile(projectConfig, templateFile, generatedDirectory);
                    break;

                case Template.ManagerProject.CustomManagerFileName:
                    GenerateCustomManagerFile(projectConfig, templateFile, generatedDirectory);
                    break;

                case Template.ManagerProject.StoredProcManagerFile:
                    GenerateStoredProcFile(projectConfig, templateFile, generatedDirectory);
                    break;

                case Template.ManagerProject.CustomStoredProcManagerFile:
                    GenerateCustomStoredProcFile(projectConfig, templateFile, generatedDirectory);
                    break;
            }
        }

        private void GenerateManagerFile(ProjectSetting projectConfig, FileInfo templateFile, DirectoryInfo generatedDirectory)
        {
            // Get entity mapper template file content
            string templateContent = GeneratorHelper.ReadFromFile(templateFile.FullName);

            // Mapper file name template
            string fileNameFormat = GeneratorHelper.GetFileNameFromat(templateFile.Name);

            // Fill methods to template
            foreach (Table t in Context.DatabaseSchema.Tables)
            {
                #region { Entity name custom tag }

                // entity name, also entity type
                string entityName = t.DataType.Name;

                projectConfig.SetPropertyValue(Template.ManagerProject.CustomTag_EntityName, entityName);
                #endregion

                #region { Manager methods custom tag }

                StringBuilder sbManagerMethods = new StringBuilder();

                #region { GetCount }
                // GetCount
                string method = string.Format(Template.ManagerProject.MethodTemplate,
                                        string.Format(Template.ManagerProject.GetCount, entityName),
                                        string.Format(Template.ManagerProject.CallGetCount, entityName));
                sbManagerMethods.AppendLine(method);

                #endregion

                #region { GetByPrimaryKey }
                // GetByPrimaryKey
                List<Column> primaryColums = t.DataType.Columns.FindAll(
                    delegate(Column c)
                    {
                        return (c.IsPrimaryKey);
                    });

                foreach (Column c in primaryColums)
                {
                    method = string.Format(Template.ManagerProject.MethodTemplate,
                                            string.Format(Template.ManagerProject.GetByPrimaryKey,
                                                            entityName,
                                                            c.Name,
                                                            c.Type,
                                                            GeneratorHelper.GenerateLocalVariableName(c.Name)),
                                            string.Format(Template.ManagerProject.CallGetByPrimaryKey,
                                                            entityName,
                                                            c.Name,
                                                            GeneratorHelper.GenerateLocalVariableName(c.Name)));
                    sbManagerMethods.AppendLine(method);
                }
                #endregion

                #region { GetListByPrimaryKey }
                // GetListByPrimaryKey
                if (primaryColums.Count > 1)
                {
                    foreach (Column c in primaryColums)
                    {
                        method = string.Format(Template.ManagerProject.MethodTemplate,
                                                string.Format(Template.ManagerProject.GetListByPrimaryKey,
                                                                entityName,
                                                                c.Name,
                                                                c.Type,
                                                                GeneratorHelper.GenerateLocalVariableName(c.Name)),
                                                string.Format(Template.ManagerProject.CallGetListByPrimaryKey,
                                                                entityName,
                                                                c.Name,
                                                                GeneratorHelper.GenerateLocalVariableName(c.Name)));

                        sbManagerMethods.AppendLine(method);
                    }
                }
                #endregion

                #region { GetListByPrimaryKeyWithPaging }
                // GetListByPrimaryKeyWithPaging
                if (primaryColums.Count > 1)
                {
                    foreach (Column c in primaryColums)
                    {
                        method = string.Format(Template.ManagerProject.MethodTemplate,
                                            string.Format(Template.ManagerProject.GetListByPrimaryKeyWithPaging,
                                                        entityName,
                                                        c.Name,
                                                        c.Type,
                                                        GeneratorHelper.GenerateLocalVariableName(c.Name)),
                                            string.Format(Template.ManagerProject.CallGetListByPrimaryKeyWithPaging,
                                                        entityName,
                                                        c.Name,
                                                        GeneratorHelper.GenerateLocalVariableName(c.Name)));

                        sbManagerMethods.AppendLine(method);
                    }
                }
                #endregion

                #region { GetByForeignKey }

                // GetByForeignKeyId
                List<Association> foreignAssociations = t.DataType.Associations.FindAll(
                        delegate(Association a)
                        {
                            return (a.IsForeignKey);
                        });

                foreach (Association a in foreignAssociations)
                {
                    Column foreignColum = t.DataType.Columns.Find(
                        delegate(Column c)
                        {
                            return (c.Name == a.ThisKey);
                        });

                    method = string.Format(Template.ManagerProject.MethodTemplate,
                                            string.Format(Template.ManagerProject.GetByForeignKey,
                                                            entityName,
                                                            a.ThisKey,
                                                            foreignColum.Type,
                                                            GeneratorHelper.GenerateLocalVariableName(a.ThisKey)),
                                            string.Format(Template.ManagerProject.CallGetByForeignKey,
                                                            entityName,
                                                            a.ThisKey,
                                                            GeneratorHelper.GenerateLocalVariableName(a.ThisKey)));

                    sbManagerMethods.AppendLine(method);
                }
                #endregion

                #region { GetByForeignKeyEntity }
                // GetByForeignKeyEntity
                foreach (Association a in foreignAssociations)
                {
                    method = string.Format(Template.ManagerProject.MethodTemplate,
                                            string.Format(Template.ManagerProject.GetByForeignKeyEntity,
                                                            entityName,
                                                            a.Type,
                                                            GeneratorHelper.GenerateLocalVariableName(a.Type)),
                                            string.Format(Template.ManagerProject.CallGetByForeignKeyEntity,
                                                            entityName,
                                                            a.Type,
                                                            GeneratorHelper.GenerateLocalVariableName(a.Type)));
                    sbManagerMethods.AppendLine(method);
                }
                #endregion

                #region { GetByForeignKeyWithPaging }

                // GetByForeignKeyWithPaging
                foreach (Association a in foreignAssociations)
                {
                    Column foreignColumn = t.DataType.Columns.Find(
                            delegate(Column c)
                            {
                                return (c.Name == a.ThisKey);
                            });

                    if (foreignColumn != null)
                    {
                        method = string.Format(Template.ManagerProject.MethodTemplate,
                                                string.Format(Template.ManagerProject.GetByForeignKeyWithPaging,
                                                            entityName,
                                                            a.ThisKey,
                                                            GeneratorHelper.GetTypeFromColumn(foreignColumn),
                                                            GeneratorHelper.GenerateLocalVariableName(a.ThisKey)),
                                                string.Format(Template.ManagerProject.CallGetByForeignKeyWithPaging,
                                                            entityName,
                                                            a.ThisKey,
                                                            GeneratorHelper.GenerateLocalVariableName(a.ThisKey)));
                        sbManagerMethods.AppendLine(method);
                    }
                }
                #endregion

                #region { GetByForeignKeyEntityWithPaging }
                // GetByForeignKeyEntityWithPaging
                foreach (Association a in foreignAssociations)
                {
                    method = string.Format(Template.ManagerProject.MethodTemplate,
                                        string.Format(Template.ManagerProject.GetByForeignKeyEntityWithPaging,
                                                        entityName,
                                                        a.Type,
                                                        GeneratorHelper.GenerateLocalVariableName(a.Type)),
                                        string.Format(Template.ManagerProject.CallGetByForeignKeyEntityWithPaging,
                                                        entityName,
                                                        a.Type,
                                                        GeneratorHelper.GenerateLocalVariableName(a.Type)));

                    sbManagerMethods.AppendLine(method);
                }
                #endregion

                #region { GetAll }
                // GetAll
                method = string.Format(Template.ManagerProject.MethodTemplate,
                                        string.Format(Template.ManagerProject.GetAll, entityName),
                                        string.Format(Template.ManagerProject.CallGetAll, entityName));
                sbManagerMethods.AppendLine(method);
                #endregion

                #region { GetAllWithPaging }
                // GetAllWithPaging
                method = string.Format(Template.ManagerProject.MethodTemplate,
                                        string.Format(Template.ManagerProject.GetAllWithPaging, entityName),
                                        string.Format(Template.ManagerProject.CallGetAllWithPaging, entityName));
                sbManagerMethods.AppendLine(method);
                #endregion

                #region { Add }
                // Add
                method = string.Format(Template.ManagerProject.MethodTemplate,
                                        string.Format(Template.ManagerProject.Add, entityName),
                                        string.Format(Template.ManagerProject.CallAdd, entityName));
                sbManagerMethods.AppendLine(method);
                #endregion

                #region { Update }
                // Update
                method = string.Format(Template.ManagerProject.MethodTemplate,
                                        string.Format(Template.ManagerProject.Update, entityName),
                                        string.Format(Template.ManagerProject.CallUpdate, entityName));
                sbManagerMethods.AppendLine(method);
                #endregion

                #region { DeleteByPrimaryKey }

                foreach (Column c in primaryColums)
                {
                    method = string.Format(Template.ManagerProject.MethodTemplate,
                                            string.Format(Template.ManagerProject.DeleteByPrimaryKey,
                                                        entityName,
                                                        c.Name,
                                                        GeneratorHelper.GetTypeFromColumn(c),
                                                        GeneratorHelper.GenerateLocalVariableName(c.Name)),
                                            string.Format(Template.ManagerProject.CallDeleteByPrimaryKey,
                                                        entityName,
                                                        c.Name,
                                                        GeneratorHelper.GenerateLocalVariableName(c.Name)));

                    sbManagerMethods.AppendLine(method);
                }
                #endregion

                #region { DeleteByPrimaryEntity }

                method = string.Format(Template.ManagerProject.MethodTemplate,
                                        string.Format(Template.ManagerProject.DeleteByPrimaryEntity,
                                                entityName,
                                                GeneratorHelper.GenerateLocalVariableName(entityName)),
                                        string.Format(Template.ManagerProject.CallDeleteByPrimaryEntity,
                                                entityName,
                                                GeneratorHelper.GenerateLocalVariableName(entityName)));

                sbManagerMethods.AppendLine(method);
                #endregion

                #region { DeleteByForeignKey }

                foreach (Association a in foreignAssociations)
                {
                    Column associationColumn = t.DataType.Columns.Find(
                                              delegate(Column c)
                                              {
                                                  return (c.Name == a.ThisKey);
                                              });

                    if (associationColumn == null)
                    {
                        continue;
                    }

                    method = string.Format(Template.ManagerProject.MethodTemplate,
                                            string.Format(Template.ManagerProject.DeleteByForeignKey,
                                                    entityName,
                                                    a.ThisKey,
                                                    GeneratorHelper.GetTypeFromColumn(associationColumn),
                                                    GeneratorHelper.GenerateLocalVariableName(a.ThisKey)),
                                            string.Format(Template.ManagerProject.CallDeleteByForeignKey,
                                                    entityName,
                                                    a.ThisKey,
                                                    GeneratorHelper.GenerateLocalVariableName(a.ThisKey)));

                    sbManagerMethods.AppendLine(method);
                }
                #endregion

                #region { DeleteByForeignEntity }

                foreach (Association a in foreignAssociations)
                {
                    method = string.Format(Template.ManagerProject.MethodTemplate,
                                        string.Format(Template.ManagerProject.DeleteByForeignEntity,
                                                    entityName,
                                                    a.Type,
                                                    GeneratorHelper.GenerateLocalVariableName(a.Type)),
                                        string.Format(Template.ManagerProject.CallDeleteByForeignEntity,
                                                    entityName,
                                                    a.Type,
                                                    GeneratorHelper.GenerateLocalVariableName(a.Type)));

                    sbManagerMethods.AppendLine(method);
                }
                #endregion

                projectConfig.SetPropertyValue(
                    Template.ManagerProject.CustomTag_EntityManagerMethods,
                    GeneratorHelper.PlaceOpenClose(sbManagerMethods.ToString()));
                #endregion

                // Fill property in template
                string sourceCode = GeneratorHelper.FillProperties(projectConfig, templateContent);

                // Write to source file
                string generatedFilePath = string.Format("{0}\\{1}",
                            generatedDirectory.FullName,
                            string.Format(fileNameFormat, t.DataType.Name));

                GeneratorHelper.WriteToFile(generatedFilePath, sourceCode);
            }
        }

        private void GenerateCustomManagerFile(ProjectSetting projectConfig, FileInfo templateFile, DirectoryInfo generatedDirectory)
        {
            // Get entity mapper template file content
            string templateContent = GeneratorHelper.ReadFromFile(templateFile.FullName);

            // Mapper file name template
            string fileNameFormat = GeneratorHelper.GetFileNameFromat(templateFile.Name);

            // Fill methods to template
            foreach (Table t in Context.DatabaseSchema.Tables)
            {
                #region { Entity name custom tag }

                // entity name, also entity type
                string entityName = t.DataType.Name;

                projectConfig.SetPropertyValue(Template.ManagerProject.CustomTag_EntityName, entityName);
                #endregion

                // Fill property in template
                string sourceCode = GeneratorHelper.FillProperties(projectConfig, templateContent);

                // Write to source file
                string generatedFilePath = string.Format("{0}\\{1}",
                            generatedDirectory.FullName,
                            string.Format(fileNameFormat, t.DataType.Name));

                GeneratorHelper.WriteToFile(generatedFilePath, sourceCode);
            }
        }

        private void GenerateStoredProcFile(ProjectSetting projectConfig, FileInfo templateFile, DirectoryInfo generatedDirectory)
        {
            // Get entity mapper template file content
            string templateContent = GeneratorHelper.ReadFromFile(templateFile.FullName);

            // Mapper file name template
            string fileNameFormat = GeneratorHelper.GetFileNameFromat(templateFile.Name);

            StringBuilder sbStoredProcedureMethods = new StringBuilder();

            // Fill methods to template
            foreach (Function f in Context.DatabaseSchema.Functions)
            {
                StringBuilder sbParameters = new StringBuilder();
                StringBuilder sbCallParameters = new StringBuilder();

                foreach (Parameter p in f.Parameters)
                {
                    if (sbParameters.Length > 0)
                    {
                        sbParameters.Append(", ");
                        sbCallParameters.Append(", ");
                    }

                    sbParameters.AppendFormat(
                        Template.Global.Declaration,
                        (p.Direction == Template.Global.ParameterDirectionInOut) ? "ref" : string.Empty,
                        GeneratorHelper.GetNullableType(p.Type),
                        p.Param);

                    if (p.Direction == Template.Global.ParameterDirectionInOut)
                    {
                        sbCallParameters.Append("ref " + p.Param);
                    }
                    else
                    {
                        sbCallParameters.Append(p.Param);
                    }
                }

                string method = string.Format(
                                        Template.ManagerProject.StoredProcedure,
                                        f.Method,
                                        GeneratorHelper.GetReturnType(Context, f),
                                        sbParameters.ToString());

                string callStore = string.Format(
                                        Template.ManagerProject.CallStoredProcedure,
                                        f.Method,
                                        sbCallParameters.ToString());

                sbStoredProcedureMethods.AppendLine(string.Format(
                                        Template.ManagerProject.MethodTemplate,
                                        method,
                                        callStore));
            }

            projectConfig.SetPropertyValue(
                                Template.ManagerProject.CustomTag_StoredProcedureMethods,
                                GeneratorHelper.PlaceOpenClose(sbStoredProcedureMethods.ToString()));

            // Fill property in template
            string sourceCode = GeneratorHelper.FillProperties(projectConfig, templateContent);

            // Write to source file
            string generatedFilePath = string.Format("{0}\\{1}",
                        generatedDirectory.FullName,
                        fileNameFormat);

            GeneratorHelper.WriteToFile(generatedFilePath, sourceCode);
        }

        private void GenerateCustomStoredProcFile(ProjectSetting projectConfig, FileInfo templateFile, DirectoryInfo generatedDirectory)
        {
            string templateContent = GeneratorHelper.ReadFromFile(templateFile.FullName);
            string fileNameFormat = GeneratorHelper.GetFileNameFromat(templateFile.Name);

            // Fill property in template
            string sourceCode = GeneratorHelper.FillProperties(projectConfig, templateContent);

            // Write to source file
            string generatedFilePath = string.Format("{0}\\{1}",
                        generatedDirectory.FullName,
                        string.Format(fileNameFormat, string.Empty));

            GeneratorHelper.WriteToFile(generatedFilePath, sourceCode, true);
        }
    }
}
